package ligne2vie.topos.shared.bo;

import java.io.Serializable;

import ligne2vie.topos.client.maps.MyMapWidget;
import ligne2vie.topos.client.maps.proxy.MyMarker;

import com.google.gwt.maps.client.base.HasLatLng;
import com.google.gwt.maps.client.base.LatLng;

/**
 * Data Transfert Object from BO to Client.
 * 
 * @author thuguerre
 * @since 1.0.0
 */
public abstract class TMapPoint implements Serializable {

	/**
	 * Default serial UID.
	 */
	private static final long serialVersionUID = 3630989179030383698L;

	protected long id;
	protected String name;
	protected double latitude;
	protected double longitude;
	protected int altitude;
	protected String description;

	/**
	 * Contains the marker to display on the map.
	 * 
	 * @since 1.1.0
	 */
	protected MyMarker marker = null;

	public String getDescription() {
		return description;
	}

	public void setDescription(String description) {
		this.description = description;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public double getLatitude() {
		return latitude;
	}

	public void setLatitude(double latitude) {
		this.latitude = latitude;
	}

	public double getLongitude() {
		return longitude;
	}

	public void setLongitude(double longitude) {
		this.longitude = longitude;
	}

	public int getAltitude() {
		return altitude;
	}

	public long getId() {
		return id;
	}

	public void setId(long id) {
		this.id = id;
	}

	public void setAltitude(int altitude) {
		this.altitude = altitude;
	}

	/**
	 * Returns this point's position on a map, by instantiating a {@link HasLatLng} object, using owned latitude and
	 * longitude.
	 * 
	 * @return Position on a map.
	 */
	public HasLatLng getPosition() {
		return new LatLng(latitude, longitude);
	}

	/**
	 * The method {@link #getMarker()} returns the marker to display on the map in order to localize the current map
	 * point. The marker is singleton pattern designed. By default, the marker is linked to the map returned by the
	 * method {@link MyMapWidget}.getMap().
	 * 
	 * @return Marker to display on the map.
	 * @since 1.1.0
	 */
	public abstract MyMarker getMarker();

	/**
	 * The method {@link #getIconPath()} returns the path to the icon image file.
	 * 
	 * @return Path to the icon image file.
	 * @since 1.1.0
	 */
	public abstract String getIconPath();

	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + (int) (id ^ (id >>> 32));
		return result;
	}

	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		TMapPoint other = (TMapPoint) obj;
		if (id != other.id)
			return false;
		return true;
	}

	/**
	 * Verifies if all parameters given to create or edit a map point are valid. Throws a {@link RuntimeException} else.
	 * 
	 * @param name
	 *            Map point's name to verify.
	 * @param altitude
	 *            Map point's altitude to verify.
	 * @param latitude
	 *            Map point's latitude to verify.
	 * @param longitude
	 *            Map point's longitude to verify.
	 * @param description
	 *            Map point's description to verify.
	 */
	public static void areParametersValid(String name, int altitude, double latitude, double longitude,
			String description) {

		if (!isNameValid(name)) {
			throw new RuntimeException("Invalid name.");
		}

		if (!isAltitudeValid(String.valueOf(altitude))) {
			throw new RuntimeException("Invalid altitude.");
		}

		if (!isLatitudeValid(latitude)) {
			throw new RuntimeException("Invalid latitude.");
		}

		if (!isLongitudeValid(longitude)) {
			throw new RuntimeException("Invalid longitude.");
		}

		if (!isDescriptionValid(description)) {
			throw new RuntimeException("Invalid description.");
		}
	}

	/**
	 * The method {@link #getInfoWindowContent()} returns the HTML content to display into an info window in order to
	 * give information about the current map point. This method provides a default method for all map points, but could
	 * not be correct for all them. Should be overridden if required.
	 * 
	 * @return Info Window HTML content.
	 * @since 1.1.0
	 */
	public String getInfoWindowContent() {

		String iwContent = "<b>" + getName() + "</b> (" + getAltitude() + " m&egrave;tres)";
		iwContent += "<br /><br />";
		iwContent += CoordinatesConversion.fromLatitude(getLatitude()).toString() + "<br />";
		iwContent += CoordinatesConversion.fromLongitude(getLongitude()).toString();

		if (getDescription() != null) {
			iwContent += "<br /><br />";
			iwContent += getDescription();
		}

		return iwContent.toString();
	}

	/**
	 * The method {@link #getShortDescription()} returns an HTML description of the current map point containing minimal
	 * information in order to recognize it. The result of this method can be used in order to display a list of map
	 * points without having too many useless information.
	 * 
	 * @return HTML short description of the current map point.
	 * @since 1.1.0
	 */
	public String getShortDescription() {

		String desc = "<b>" + getName() + "</b> (" + getAltitude() + " m&egrave;tres)";
		desc += "<br />";
		desc += CoordinatesConversion.fromLatitude(getLatitude()).toString();
		desc += " - ";
		desc += CoordinatesConversion.fromLongitude(getLongitude()).toString();

		return desc;
	}

	/**
	 * Verify if the altitude is valid to be then stored in database.
	 * 
	 * @param altitude
	 *            Altitude to verify.
	 * @return <code>true</code> if valid. <code>false</code> else.
	 */
	public static boolean isAltitudeValid(String altitude) {

		if (altitude == null || altitude.trim().length() == 0) {
			return false;
		}

		try {
			int intAltitude = Integer.valueOf(altitude);

			if (intAltitude < 1 || intAltitude > 8850) {

				return false;

			}

		} catch (NumberFormatException ex) {

			return false;
		}

		return true;
	}

	/**
	 * Verify if the description is valid to be then stored in database.
	 * 
	 * @param description
	 *            Description to verify.
	 * @return <code>true</code> if valid. <code>false</code> else.
	 */
	public static boolean isDescriptionValid(String description) {

		return true;
	}

	/**
	 * Verify if the latitude is valid to be then stored in database.
	 * 
	 * @param latitude
	 *            Latitude to verify.
	 * @return <code>true</code> if valid. <code>false</code> else.
	 */
	public static boolean isLatitudeValid(double latitude) {

		return -90 <= latitude && latitude <= 90;
	}

	/**
	 * Verify if the longitude is valid to be then stored in database.
	 * 
	 * @param longitude
	 *            Longitude to verify.
	 * @return <code>true</code> if valid. <code>false</code> else.
	 */
	public static boolean isLongitudeValid(double longitude) {

		return -180 <= longitude && longitude <= 180;
	}

	/**
	 * Verify if the name is valid to be then stored in database.
	 * 
	 * @param name
	 *            Name to verify.
	 * @return <code>true</code> if valid. <code>false</code> else.
	 */
	public static boolean isNameValid(String name) {

		return name != null && name.trim().length() != 0;
	}
}
